﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Media.Imaging;
using System.ComponentModel;

using HelixToolkit.Wpf;
using EQEmu.Display3D;
using EQEmu.ZonePoints;
using EQEmu.Database;
using EQEmu;

namespace EQEmu.ZonePoints.Display3D
{
    public sealed class ZonePointsDisplay3D : IDisposable, IViewConstrainer
    {
        private ZonePoints _zp;
        private GeometryModel3D _zoneExitModel;
        private GeometryModel3D _zoneDestinationModel;
        private ViewConstraints _constraints = null;

        private Model3D _model = new Model3DGroup();
        private Dictionary<zone_points, Model3DCollection> _mapping = new Dictionary<zone_points, Model3DCollection>();

        private double _boxSize = 20.0;

        public ZonePointsDisplay3D(ZonePoints zp)
        {
            _zp = zp;
            _zp.ZonePointsCollectionChanged += _zp_ZonePointsCollectionChanged;
            MeshBuild();

            foreach (var p in zp.Points)
            {
                CreateZonePoint(p);
            }
        }

        public double BoxSize
        {
            get { return _boxSize; }
        }

        private void MeshBuild()
        {
            MeshBuilder builder = new MeshBuilder();
            builder.AddBox(new Point3D(0, 0, 0), BoxSize, BoxSize, 5);            
            _zoneExitModel = new GeometryModel3D(builder.ToMesh(), Materials.Yellow);

            builder = new MeshBuilder();
            builder.AddBox(new Point3D(0, 0, 0), BoxSize, BoxSize, 5);
            _zoneDestinationModel = new GeometryModel3D(builder.ToMesh(), Materials.Red);
        }

        void _zp_ZonePointsCollectionChanged(object sender, ZonePointsChangedEventArgs e)
        {
            if (e.Added != null)
            {
                foreach (var p in e.Added)
                {
                    CreateZonePoint(p);
                }
            }

            if (e.Removed != null)
            {
                foreach (var p in e.Removed)
                {
                    RemoveZonePoint(p);
                }
            }
        }

        private void CreateZonePoint(zone_points zp)
        {
            Model3DGroup group = Model as Model3DGroup;
            var point = zp as INotifyPropertyChanged;

            Model3DCollection collection = new Model3DCollection();

            if (_mapping.ContainsKey(zp) && _mapping[zp] != null)
            {
                foreach (Model3D model in _mapping[zp])
                {
                    group.Children.Remove(model);
                }
            }
            else
            {
                if (point != null)
                {
                    point.PropertyChanged += point_PropertyChanged;
                }
            }

            _mapping[zp] = collection;

            Transform3DGroup transforms = new Transform3DGroup();
            transforms.Children.Add(new TranslateTransform3D(zp.x, zp.y, zp.z));
            var exitPoint = _zoneExitModel.Clone();
            exitPoint.Transform = new TranslateTransform3D(zp.x, zp.y, zp.z);
            var destPoint = _zoneDestinationModel.Clone();
            destPoint.Transform = new TranslateTransform3D(zp.target_x, zp.target_y, zp.target_z);
            collection.Add(exitPoint);
            collection.Add(destPoint);

            var builder = new MeshBuilder();
            builder.AddArrow(new Point3D(zp.x, zp.y, zp.z), new Point3D(zp.target_x, zp.target_y, zp.target_z), 5);
            collection.Add(new GeometryModel3D(builder.ToMesh(), Materials.White));

            foreach (Model3D model in collection)
            {
                group.Children.Add(model);
            }     
        }

        void point_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            CreateZonePoint(sender as zone_points);
        }

        private void RemoveZonePoint(zone_points zp)
        {
            var changer = zp as INotifyPropertyChanged;
            if (changer != null)
            {
                changer.PropertyChanged -= point_PropertyChanged;
            }

            Model3DGroup group = Model as Model3DGroup;
            foreach (Model3D model in _mapping[zp])
            {
                group.Children.Remove(model);
            }
            _mapping[zp] = null;
        }

        protected void Dispose(bool disposeAll)
        {
            if (_zp != null)
            {
                _zp.ZonePointsCollectionChanged -= _zp_ZonePointsCollectionChanged;
            }

            foreach (var zp in _zp.Points.Cast<INotifyPropertyChanged>())
            {
                zp.PropertyChanged -= point_PropertyChanged;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        public void UpdateView(ViewConstraints constraints)
        {
            _constraints = constraints;
        }

        public Model3D Model
        {
            get
            {
                return _model;
            }
        }
    }
}
